/*
! Program for solid mechanics using FEM
!
!
! Author: Dr. Chennakesava Kadapa
! Date  : 23-November-2023
! Place : Edinburgh, UK
!
!
*/


#include "headersVTK.h"
#include "headersBasic.h"
#include "femSolids.h"
#include <string>
#include <unistd.h>
#include "Global.h"
#include "log.h"
#include <boost/algorithm/string.hpp>

using namespace std;



int main(int argc, char* argv[])
{

    if(argc == 0) {
        PetscPrintf(MPI_COMM_WORLD, "\n    No input file is specified ... \n ");
        PetscPrintf(MPI_COMM_WORLD, "\n    Aborting the program ... \n ");
        return 1;
    }

    //
    // PETSc options file
    //
    string  petscfile     = "petsc_options.dat";

    ifstream finpetscfile(petscfile);
    if(finpetscfile.fail())
    {
      PetscPrintf(MPI_COMM_WORLD, "\n    PETSc options file is not available ... \n ");
      exit(1);
    }
    PetscInitialize(NULL, NULL, petscfile.c_str(), NULL);
    
    //
    // Input file
    //
    string  inpfilename = argv[1];

    PetscPrintf(MPI_COMM_WORLD, "\n\n\n    Input file ... %s \n\n\n", inpfilename.c_str());

    ifstream fin;

    // get the input file extension to choose the reader function
    vector<string>  stringlist;
    boost::algorithm::split(stringlist, inpfilename, boost::is_any_of("."), boost::token_compress_on);
    for(auto& str: stringlist)  boost::trim(str);

    string ext = stringlist[stringlist.size() - 1];

    femSolids solidfem(inpfilename);
    string    read_file_op;   
    COMMON_ERR_CODE err_code;
    PetscErrorCode  err_petsc;
    /*** init ***/ 
    Logger::getInstance().setLogLevel(LogLevel::INFO);
    solidfem.setJobName(stringlist[0]);
    struct timeval start, end;

    #pragma omp parallel
    {
        int thread_id   = omp_get_thread_num();
        int num_threads = omp_get_num_threads();
        LOG_INFO("omp parallel from thread " + to_string(thread_id) + " to " + to_string(num_threads));
    }

    gettimeofday(&start, NULL);
    // /*** read common ***/ 
    // fin.open(inpfilename);
    // if(fin.fail()) {
    //     LOG_ERROR("Data file open failed" + string(__FILE__) + ":" + to_string(__LINE__));
    // }
    // read_file_op = "read_common";
    // err_code = solidfem.read_file_op(fin, read_file_op);
    // if (err_code != COMMON_SUCCESS) {
    //     LOG_ERROR("Read common info failed! err_code: " + to_string(err_code) 
    //         + string(__FILE__) + ":" + to_string(__LINE__));
    //     exit(1);
    // }
    // fin.close();
    // LOG_INFO("Read common info end " + string(__FILE__) + ":" + to_string(__LINE__));

    fin.open(inpfilename);
    if(fin.fail()) {
        LOG_ERROR("Data file open failed" + string(__FILE__) + ":" + to_string(__LINE__));
    }
    read_file_op = "read_common1";
    err_code = solidfem.read_file_op1(fin, read_file_op);
    if (err_code != COMMON_SUCCESS) {
        LOG_ERROR("Read common info failed! err_code: " + to_string(err_code) 
            + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    fin.close();
    LOG_INFO("Read global " + string(__FILE__) + ":" + to_string(__LINE__));


    
    /*** read element pre ***/
    fin.open(inpfilename);
    if(fin.fail()) {
        LOG_ERROR("file open failed" + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    read_file_op = "read_element_pre";
    err_code = solidfem.read_file_op(fin, read_file_op);
    if (err_code != COMMON_SUCCESS) {
        LOG_ERROR("Read element pre failed! err_code: " + to_string(err_code) 
            + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    fin.close();
    LOG_INFO("Read element pre end " + string(__FILE__) + ":" + to_string(__LINE__));

    MPI_Barrier(MPI_COMM_WORLD);

    /*** read common ***/ 
    fin.open(inpfilename);
    if(fin.fail()) {
        LOG_ERROR("Data file open failed" + string(__FILE__) + ":" + to_string(__LINE__));
    }
    read_file_op = "read_common";
    err_code = solidfem.read_file_op(fin, read_file_op);
    if (err_code != COMMON_SUCCESS) {
        LOG_ERROR("Read common info failed! err_code: " + to_string(err_code) 
            + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    fin.close();
    LOG_INFO("Read common info end " + string(__FILE__) + ":" + to_string(__LINE__));

    /*** split element ***/
    err_petsc = solidfem.splitSolidElements();
    if (err_petsc != PETSC_SUCCESS) {
        LOG_ERROR("Split element failed! err_code: " + to_string(err_petsc) 
            + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    LOG_INFO("Split element end " + string(__FILE__) + ":" + to_string(__LINE__));

    /*** send&recv local element ***/
    err_petsc = solidfem.recvLocalElements();
    if (err_petsc != PETSC_SUCCESS) {
        LOG_ERROR("Send&Recv local element failed! err_code: " + to_string(err_petsc) 
            + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    LOG_INFO("Send&Recv local element end " + string(__FILE__) + ":" + to_string(__LINE__));

    /*** read element local ***/
    fin.open(inpfilename);
    if(fin.fail()) {
        LOG_ERROR("Data file open failed" + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    read_file_op = "read_element_local";
    err_code = solidfem.read_file_op(fin, read_file_op);
    if (err_code != COMMON_SUCCESS) {
        LOG_ERROR("Read element local failed! err_code: " + to_string(err_code) 
            + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    fin.close();
    LOG_INFO("Read element local end " + string(__FILE__) + ":" + to_string(__LINE__));

    /*** read node local ***/
    fin.open(inpfilename);
    if(fin.fail()) {
        LOG_ERROR("Data file open failed" + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    read_file_op = "read_node_local";
    err_code = solidfem.read_file_op(fin, read_file_op);
    if (err_code != COMMON_SUCCESS) {
        LOG_ERROR("Read node local failed! err_code: " + to_string(err_code) 
            + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    fin.close();
    LOG_INFO("Read node local end " + string(__FILE__) + ":" + to_string(__LINE__));

    finpetscfile.close();

    gettimeofday(&end, NULL);
    double read_time = get_time_fem(&start, &end);
    LOG_INFO("read end cost time(s): " + to_string(read_time));

    FILE *file;
    char path[50];
    long pages, dummy, data, stack, text, total_vm, rss;
    sprintf(path, "/proc/%d/statm", getpid());
    file = fopen(path, "r");
    if (file != NULL) {
        fscanf(file, "%ld %ld %ld %ld %ld %ld",
           &total_vm, &data, &stack, &text, &dummy, &rss);
        fclose(file);

        pages = sysconf(_SC_PAGESIZE) / 1024;
        double rss_mb = rss * pages / 1024.0;

        LOG_INFO("read end RSS in usage: " + to_string(rss_mb * 1e-3) + " Gbytes");
    }

    gettimeofday(&start, NULL);
    err_code = solidfem.prepareInputData();
    if (err_code != COMMON_SUCCESS) {
        LOG_ERROR("Prepare data failed! err_code: " + to_string(err_code) 
            + string(__FILE__) + ":" + to_string(__LINE__));
        exit(1);
    }
    gettimeofday(&end, NULL);
    double pre_time = get_time_fem(&start, &end);
    LOG_INFO("prepare data end cost time(s): " + to_string(pre_time));

    file = fopen(path, "r");
    if (file != NULL) {
        fscanf(file, "%ld %ld %ld %ld %ld %ld",
           &total_vm, &data, &stack, &text, &dummy, &rss);
        fclose(file);

        pages = sysconf(_SC_PAGESIZE) / 1024;
        double rss_mb = rss * pages / 1024.0;

        LOG_INFO("prepare data end RSS in usage: " + to_string(rss_mb * 1e-3) + " Gbytes");
    }

    solidfem.setSolver(2);

    gettimeofday(&start, NULL);
    solidfem.solveFullyImplicit();
    gettimeofday(&end, NULL);
    double solve_time = get_time_fem(&start, &end);
    LOG_INFO("total solve end cost time(s): " + to_string(solve_time));

    PetscPrintf(MPI_COMM_WORLD, "\n\n\n Program is successful \n\n\n ");

    solidfem.deallocatePetscObjects();

    MPI_Barrier(MPI_COMM_WORLD);
    PetscFinalize();
    return 0;
}
